package com.lunz.securityproject.conf;

import com.lunz.securityproject.conf.handle.*;
import com.lunz.securityproject.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * @Author 施瑞贤
 * @email shiruixian@zhongruigroup.com
 * @date 2021/11/5 16:18
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //匿名用户访问无权限资源时的异常4
    @Autowired
    CustomizeAuthenticationEntryPoint authenticationEntryPoint;

    //登录成功处理逻辑1
    @Autowired
    CustomizeAuthenticationSuccessHandler authenticationSuccessHandler;

    //登录失败处理逻辑2
    @Autowired
    CustomizeAuthenticationFailureHandler authenticationFailureHandler;

    //权限拒绝处理逻辑3
    @Autowired
    CustomizeAccessDeniedHandler accessDeniedHandler;

    //登出成功处理逻辑6
    @Autowired
    CustomizeLogoutSuccessHandler logoutSuccessHandler;

    //会话失效(账号被挤下线)处理逻辑5
    @Autowired
    CustomizeSessionInformationExpiredStrategy sessionInformationExpiredStrategy;

    //访问决策管理器7
    @Autowired
    CustomizeAccessDecisionManager accessDecisionManager;

    //实现权限拦截8
    @Autowired
    CustomizeFilterInvocationSecurityMetadataSource securityMetadataSource;

    //权限拦截器9
    @Autowired
    private CustomizeAbstractSecurityInterceptor securityInterceptor;

    //导入数据源，实现记住我
    @Autowired
    private DataSource dataSource;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置认证方式等
        auth.userDetailsService(userDetailsService());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //http相关的配置，包括登入登出、异常处理、会话管理等
        //开启跨域
        http.cors();
        //关闭csrf
        http.csrf().disable();
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        //决策管理器
                        o.setAccessDecisionManager(accessDecisionManager);
                        //安全元数据源
                        o.setSecurityMetadataSource(securityMetadataSource);
                        return o;
                    }
                })
                //异常处理(权限拒绝、登录失效等)
                .and().exceptionHandling()
                //匿名用户访问无权限资源时的异常处理;
                .authenticationEntryPoint(authenticationEntryPoint)
                //权限不足拒绝处理
                .accessDeniedHandler(accessDeniedHandler)

                //登出允许所有用户
                .and().logout().permitAll()
                //登出成功处理逻辑
                .logoutSuccessHandler(logoutSuccessHandler)
                //登出之后删除cookie
                .deleteCookies("JSESSIONID")
                //使HttpSession失效
                .invalidateHttpSession(true)

                //登入允许所有用户
                .and().formLogin().permitAll()
                //登录成功处理逻辑
                .successHandler(authenticationSuccessHandler)
                //登录失败处理逻辑
                .failureHandler(authenticationFailureHandler)

                //记住我
                .and().rememberMe()
                //设置前端传过来的记住我的名称
                .rememberMeParameter("remember_me")
                //使用token仓库
                .tokenRepository(persistentTokenRepository())
                //设置token存在时间，单位为秒
                .tokenValiditySeconds(60 * 60 * 24 * 30)
                .userDetailsService(userDetailsService())

                //启动会话管理
                .and().sessionManagement()
                //设置最大登录数为1
                .maximumSessions(1)
                //当登录数达最大值时，是否保留已登录用户（true：保留，新用户无法登录；false：不保留，踢出旧用户）
                .maxSessionsPreventsLogin(false)
                //处理账号失效、被挤下线
                .expiredSessionStrategy(sessionInformationExpiredStrategy)
                //踢出用户配置
                .sessionRegistry(sessionRegistry());

                //将权限拦截器增加到默认拦截链中
                http.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class);

                //添加过滤器，放行options等
                //ChannelProcessingFilter为决定 web请求的通道，即 http或 https
                http.addFilterAfter(optionsFilter(), ChannelProcessingFilter.class);

                //添加vaptcha登录验证过滤器
                http.addFilterBefore(vaptchaFilterr(), UsernamePasswordAuthenticationFilter.class);

    }


    /**
     * 添加踢出用户配置
     * @return
     */
    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    /**
     * 添加 vaptcha验证过滤器
     * @return
     */
    @Bean
    public VaptchaFilterr vaptchaFilterr(){
        return new VaptchaFilterr();
    }

    /**
     * 实现记住我
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        //在第一次运行时会创建一个记住我token数据库，只能运行一次
//        tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }

    /**
     * 配置过滤器，放行options等
     * @return
     */
    @Bean
    public OptionsFilter optionsFilter(){
        return new OptionsFilter();
    }

    /**
     * 获取用户账号密码及权限信息
     * @return
     */
    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        return new UserDetailsServiceImpl();
    }

    /**
     * 设置密码加密方式
     * @return
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        // 设置默认的加密方式（强hash方式加密）
        return new BCryptPasswordEncoder();
    }

}
